package com.itools.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itools.core.base.CommonResult;
import com.itools.core.bean.BeanCopyUtils;
import com.itools.core.dto.accessToken.FmsAccessTokenDTO;
import com.itools.core.dto.fileRecord.FmsRecordDTO;
import com.itools.core.em.FmsCodeBean;
import com.itools.core.em.FmsConstants;
import com.itools.core.exception.AppException;
import com.itools.core.log.Logable;
import com.itools.core.mapper.FmsAccessTokenMapper;
import com.itools.core.mapper.FmsFileRecordMapper;
import com.itools.core.param.FmsAccessTokenParam;
import com.itools.core.param.FmsUploadTokenParam;
import com.itools.core.param.FmsBusinessTypeParam;
import com.itools.core.request.FmsBase64FileUploadRequest;
import com.itools.core.result.*;
import com.itools.core.service.FmsFileSystemService;
import com.itools.core.snowflake.SequenceService;
import com.itools.core.utils.Base64Utils;
import com.itools.core.utils.CollectionUtils;
import com.itools.core.utils.UUIDUtils;
import com.itools.core.utils.ZipUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: XUCHANG
 * @create: 2021-03-28 15:51
 */
@Service
public class FmsFileSystemServiceImpl implements FmsFileSystemService {

    @Value("${fms.file.store-root-path}")
    String fileStoreRootPath;

    @Value("${fms.file.access-url-for-outter}")
    String fileAccessUrlForOutter;

    @Value("${fms.file.upload-limit-size}")
    Integer fileUploadLimitSize;

    @Value("${fms.file.max-request-size}")
    Integer fileMaxRequestSize;

    @Value("${fms.file.max-file-size}")
    Integer fileMaxFileSize;

    @Value("${fms.token-expiration-time}")
    Integer tokenExpirationTime;

    @Autowired
    FmsFileSystemServiceImpl self;
    
    @Autowired
    private SequenceService sequenceService;

    @Autowired
    private FmsBusinessTypeServiceImpl fmsBusinessTypeService;
    
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private FmsFileRecordMapper fmsFileRecordMapper;
    
    @Autowired
    private FmsAccessTokenServiceImpl fmsAccessTokenService;

    @Autowired
    private FmsAccessTokenMapper fmsAccessTokenMapper;

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    //文件的type
    private final static String CURRENCY_TYPE = "currency";
    //线程池：核心线程：10，非核心线程：40，阻塞队列：100
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 50, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(100));

    /**
     * 根据uniqeId和文件名得到本地文件存储路径
     * @param uniqueId
     * @param fileName
     * @return
     */
    @Override
    public String getFileLocalPath(String uniqueId , String fileName) {
        if (StringUtils.isBlank(uniqueId)) {
            throw new AppException(FmsCodeBean.FmsCode.INVALID_FS_UNIQUE_ID.code);
        }
        String path = fileStoreRootPath+File.separator;
        int pathLength = 4;
        for(int i=0;i<pathLength;i++) {
        	path = path+ StringUtils.substring(uniqueId, i*8, 8+i*8)+File.separator;
        }
        return path + fileName;
    }


    /**
     * 存储文件
     *
     * @param file
     * @param fmsUploadTokenParam
     * @return
     */
    @Override
    @Logable
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public String saveFile(MultipartFile file, FmsUploadTokenParam fmsUploadTokenParam) {
        String uuid = UUIDUtils.uuid();
        self.saveFileToLocal(file, uuid, fmsUploadTokenParam.getOriginalFilename());
        return createAndPersistDBRecord(file, fmsUploadTokenParam, uuid);
    }

    /**
     * 存储文件
     *
     * @param file 文件
     * @param fmsUploadTokenParam 参数
     * @param uuid UUID
     * @return
     */
    @Logable
    @Transactional(rollbackFor = Exception.class)
    public String saveFile(MultipartFile file, FmsUploadTokenParam fmsUploadTokenParam, String uuid) {
        self.saveFileToLocal(file, uuid);
        return createAndPersistDBRecord(file, fmsUploadTokenParam, uuid);
    }

    /**
     * 文件持久化到数据库
     * @param originalFilename
     * @param contentType
     * @param fileSize
     * @param fmsUploadTokenParam
     * @param fileUnqieId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String createAndPersistDBRecord(String originalFilename, String contentType, Long fileSize, FmsUploadTokenParam fmsUploadTokenParam, String fileUnqieId) {
        FmsRecordDTO fssRecord = new FmsRecordDTO();
        Date date = new Date();
        fssRecord.setId(sequenceService.nextValue(FmsConstants.FSS_RECORD_ID_SEQ_NAME));
        fssRecord.setContentType(contentType);
        fssRecord.setFromSystemId(fmsUploadTokenParam.getFromSystemId());
        fssRecord.setFileSize(fileSize);
        fssRecord.setUniqueId(fileUnqieId);
        fssRecord.setRemark(fmsUploadTokenParam.getRemark());
        fssRecord.setOrigFileName(originalFilename);
        fssRecord.setCreateTime(date);
        fssRecord.setUpdateTime(date);
        fmsFileRecordMapper.insertSelective(fssRecord);
        return fileUnqieId;
	}

    /**
     * 文件持久化到数据库
     * @param file
     * @param fmsUploadTokenParam
     * @param uuid
     * @return
     */
    @Override
    public String createAndPersistDBRecord(MultipartFile file, FmsUploadTokenParam fmsUploadTokenParam, String uuid) {
		String originalFilename = file.getOriginalFilename();
		if (!StringUtils.isBlank(fmsUploadTokenParam.getOriginalFilename())){
            originalFilename = fmsUploadTokenParam.getOriginalFilename();
        }
        String contentType = file.getContentType();
        return createAndPersistDBRecord(originalFilename, contentType, file.getSize(), fmsUploadTokenParam, uuid);
	}

    /**
     * 文件写入
     * @param file
     * @param uuid
     */
    protected void saveFileToLocal(MultipartFile file, String uuid) {
		String filePath = getFileLocalPath(uuid , file.getOriginalFilename());
        File outputFile = new File(filePath);
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        try {
            outputFile.createNewFile();
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
        try (InputStream inputStream = file.getInputStream(); OutputStream out = new FileOutputStream(outputFile)) {
            FileCopyUtils.copy(inputStream, out);
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
	}
    /**
     * 文件写入
     * @param file
     * @param uuid
     */
    protected void saveFileToLocal(MultipartFile file, String uuid, String originalFilename) {
        String fileName = file.getOriginalFilename();
        if (!StringUtils.isBlank(originalFilename)){
            fileName = originalFilename;
        }
        String filePath = getFileLocalPath(uuid , fileName);
        File outputFile = new File(filePath);
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        try {
            outputFile.createNewFile();
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
        try (InputStream inputStream = file.getInputStream(); OutputStream out = new FileOutputStream(outputFile)) {
            FileCopyUtils.copy(inputStream, out);
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
    }
    
    /**
     * 申请文件上传token
     * @param businessType
     * @param fromSystemId
     * @param needEncrypt
     * @param remark
     * @return
     */
    @Override
    @Logable
    public CommonResult<String> applyUploadToken(String businessType, String fromSystemId, String needEncrypt, String remark) {
        if (StringUtils.isBlank(businessType)) {
        	throw new AppException(FmsCodeBean.FmsCode.ERROR_BUSINESS_TYPE_NOT_EXIST.code);
        }
        String uploadToken = UUIDUtils.uuid();
        List<FmsBusinessTypeResult> fsBusinessTypes = fmsBusinessTypeService.queryFsBusinessTypeList(businessType);
        if (fsBusinessTypes == null || fsBusinessTypes.isEmpty()) {
            throw new AppException(FmsCodeBean.FmsCode.ERROR_BUSINESS_TYPE_NOT_EXIST.code);
        }
        FmsUploadTokenParam fsUploadToken = new FmsUploadTokenParam();
        fsUploadToken.setFromSystemId(fromSystemId);
        fsUploadToken.setBusinessTypeResult(fsBusinessTypes.get(0));
        fsUploadToken.setNeedEncrypt(needEncrypt);
        fsUploadToken.setRemark(remark);
        //将文件申请的token写入到缓存
        try {
            String tokenParam = objectMapper.writeValueAsString(fsUploadToken);
            stringRedisTemplate.opsForValue().set((FmsConstants.FILE_TOKEN_NAME_SPACE+uploadToken),tokenParam,tokenExpirationTime,TimeUnit.MINUTES);
        } catch (JsonProcessingException e) {
            logger.error("JackJson序列化失败");
        }
        return CommonResult.success(uploadToken);
    }
    
    
    /**
     * 存储文件
     * 使用字节流输入文件
     * 返回文件的全局唯一ID
     * @param fileBytes
     * @param fileName
     * @param fsBusinessType
     * @param fsUploadToken
     * @return
     */
    @Override
    public String saveFileFromBytes(byte[] fileBytes, String fileName, FmsBusinessTypeParam fsBusinessType, FmsUploadTokenParam fsUploadToken) {
        String uniqueId = UUIDUtils.uuid();
        saveFileToLocal(fileBytes, fileName, uniqueId);

        String originalFilename = StringUtils.isBlank(fileName) ? uniqueId : fileName;
        String contentType = fsBusinessType.getFsType();
        
        return createAndPersistDBRecord(originalFilename, contentType, (long) fileBytes.length, fsUploadToken, uniqueId);
    }



    protected void saveFileToLocal(byte[] fileBytes, String fileName, String uuid) {
		File outputFile = new File(getFileLocalPath(uuid , fileName));
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        try {
            outputFile.createNewFile();
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
        try (OutputStream out = new FileOutputStream(outputFile)) {
            FileCopyUtils.copy(fileBytes, out);
        } catch (IOException e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code, e);
        }
	}

    /**
     * 获取文件访问路径
     *
     * @param uniqueId
     * @param expiredTime
     * @param maxAccessCount
     * @return
     */
    @Override
    @Logable
    public CommonResult<String> getFileUrlByFileId(String uniqueId, int expiredTime, int maxAccessCount, String type) {
        FmsRecordDTO tmp = new FmsRecordDTO();
        tmp.setUniqueId(uniqueId);
        List<FmsRecordDTO> fsUploadRecords = fmsFileRecordMapper.selectbySelective(tmp);
        if (fsUploadRecords == null || fsUploadRecords.isEmpty()) {
            logger.error(FmsCodeBean.FmsCode.NOT_EXIST_FILE.message,new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code, FmsCodeBean.FmsCode.NOT_EXIST_FILE.message));
            return CommonResult.success(null);
        }
        String fileAccessToken = UUIDUtils.uuid();
        //String fileAccessUrlForOutter = "http://192.168.0.135:8150/fms/File/";//本地路径
        String path = fileAccessUrlForOutter + uniqueId + "?fileAccessToken=" + fileAccessToken;
        // 缓存文件token
        FmsAccessTokenResult fsAccessToken = fmsAccessTokenService.cacheFileAccessToken(fileAccessToken, expiredTime, maxAccessCount, uniqueId, type);
        if (fsAccessToken == null) {
        	throw new AppException(FmsCodeBean.FmsCode.FAIL.code);
        }
        return CommonResult.success(path);
    }

    /**
     * 获得uniqueId对应的文件上传记录
     * 会检查
     * @param accessTokenParam
     * @param uniqueId
     * @param fileAccessToken
     * @return
     */
    @Override
    @Logable
    public FmsDetailRecordResult getFileUploadRecord(FmsAccessTokenParam accessTokenParam, String uniqueId, String fileAccessToken) {
        FmsRecordDTO tmp = new FmsRecordDTO();
        tmp.setUniqueId(uniqueId);
        List<FmsRecordDTO> uploadRecords = fmsFileRecordMapper.selectbySelective(tmp);
        if (uploadRecords == null || uploadRecords.isEmpty()) {
            // 无该文件，返回404
            throw new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code);
        }
        int maxAccessCount = accessTokenParam.getMaxAccessCount();
        if (accessTokenParam.getExpiredTime() > 0){
            if (maxAccessCount != -1) {
                // 次数减一
                maxAccessCount--;
                if (maxAccessCount == 0) {
                    // 删除缓存
                    fmsAccessTokenService.deleteFileAccessToken(fileAccessToken);
                } else {
                    // 更新缓存
                    int expiredTime = accessTokenParam.getExpiredTime();
                    fmsAccessTokenService.cacheFileAccessToken(fileAccessToken, expiredTime, maxAccessCount, uniqueId, accessTokenParam.getType());
                    // 更新缓存时间
                    redisTemplate.expire(fileAccessToken, expiredTime, TimeUnit.MINUTES);
                }
            }
        }

        // 返回该文件
        FmsRecordDTO fssRecord = uploadRecords.get(0);
        FmsDetailRecordResult result = new FmsDetailRecordResult();
        BeanUtils.copyProperties(fssRecord , result);
        return result;
    }

    @Override
    public FmsDetailRecordResult getFileUploadRecord(String uniqueId) {
        FmsRecordDTO tmp = new FmsRecordDTO();
        tmp.setUniqueId(uniqueId);
        List<FmsRecordDTO> fsUploadRecords = fmsFileRecordMapper.selectbySelective(tmp);
        if (fsUploadRecords == null || fsUploadRecords.isEmpty()) {
            // 无该文件，返回404
            throw new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code);
        }
        FmsRecordDTO fssRecord = fsUploadRecords.get(0);
        FmsDetailRecordResult result = new FmsDetailRecordResult();
        BeanUtils.copyProperties(fssRecord , result);
        return result;
    }

    /**
     * 根据文件唯一id查询文件的上传记录
     *
     * @param uniqueIds
     * @return
     */
    @Override
    public List<FmsDetailRecordResult> queryFileRecordsByUniqueIds(List<String> uniqueIds) {
        if (CollectionUtils.isEmpty(uniqueIds)){
            return new ArrayList<>();
        }
        FmsRecordDTO fmsRecordDTO = new FmsRecordDTO();
        fmsRecordDTO.setUniqueIds(uniqueIds);
        List<FmsRecordDTO> recordDTOS = fmsFileRecordMapper.selectbySelective(fmsRecordDTO);
        List<FmsDetailRecordResult> recordResults = BeanCopyUtils.copy(recordDTOS,FmsDetailRecordResult.class);
        return recordResults;
    }

    /**
     * 文件批量上传
     * @param files
     * @param fmsUploadTokenParam
     * @return
     */
    @Override
    public CommonResult<List<FmsMultipartFileResult>> uploadFiles(MultipartFile[] files, FmsUploadTokenParam fmsUploadTokenParam) {

        //收集文件数对应的结果
        List<FmsMultipartFileResult> results = new ArrayList<>();
        List<FutureTask<String>> taskList = new ArrayList<>();
        for (MultipartFile file : files){
            //执行多线程上传 执行上传的service，文件，参数
            String uuid = UUIDUtils.uuid();
            FutureTask<String> futureTask = new FutureTask<>(new FmsFileRecordUploadTask(self,file,fmsUploadTokenParam,uuid));
            taskList.add(futureTask);
            //返回信息
            FmsMultipartFileResult multipartFileResult = new FmsMultipartFileResult();
            multipartFileResult.setFileName(file.getOriginalFilename());
            multipartFileResult.setUniqueId(String.valueOf(uuid));
            results.add(multipartFileResult);
            try {
                threadPoolExecutor.execute(futureTask);
            }catch (Exception e){
                logger.error("批量上传文件失败");
                throw new AppException(FmsCodeBean.FmsCode.FILE_MULTIPART_UPLOAD.code);
            }
        }
        //待所有线程任务完成，返回结果。其中一个上传失败，抛出异常
        for(FutureTask<String> task: taskList){
            if (task == null){
                logger.error("批量上传文件失败");
                throw new AppException(FmsCodeBean.FmsCode.FILE_MULTIPART_UPLOAD.code);
            }
        }

        return CommonResult.success(results);
    }

    /**
     * 验证文件
     * @param files
     * @param fsBusinessType
     */
    @Override
    public void verifyFiles(MultipartFile[] files, FmsBusinessTypeResult fsBusinessType) {
        if (files.length == 0 || files == null){
            throw new AppException(FmsCodeBean.FmsCode.ERROR_FILE_IS_NULL.code);
        }
        if (files.length > fileUploadLimitSize){
            throw new AppException(FmsCodeBean.FmsCode.FILE_LIMIT_SIZE.code);
        }
        //所有文件大小,单位字节
        long allFileSize = 0;
        for (MultipartFile file : files){
            if (file == null || StringUtils.isBlank(file.getOriginalFilename())) {
                // 文件不能为空
                throw new AppException(FmsCodeBean.FmsCode.ERROR_FILE_IS_NULL.code);
            }
            //通用类型的文件大小，单位kb
            String fsSize = fsBusinessType.getFsSize();
            //通用类型的文件大小，单位kb
            Long limitSize = Long.parseLong(fsSize);
            //文件的大小,单位字节
            long fileSize = file.getSize();
            //总文件累计,单位字节
            allFileSize += fileSize;
            //类型允许的文件大小，KB转字节
            long allowFileSize = limitSize * 1024;
            //判断文件大小与限制的大小是否符合
            if (fileSize > allowFileSize || fileSize > (fileMaxFileSize * 1024)) {
                logger.error("文件名称{}的大小{}字节的文件超过限制【类型限制大小{}KB，单个文件限制大小{}KB】",file.getOriginalFilename(),fileSize,fileSize,fileMaxFileSize);
                throw new AppException(FmsCodeBean.FmsCode.ERROR_FILESIZE_OUT_OFF_LIMIT.code);
            }
            String fsType = fsBusinessType.getFsType();
            if (!StringUtils.equals(fsType, CURRENCY_TYPE)){
                //如果取值不为currency，则判断类型
                String originFileName = file.getOriginalFilename();
                String suffix = originFileName.substring(originFileName.lastIndexOf(".") + 1);
                String contentType = file.getContentType();
                if (contentType == null || !fsType.contains(suffix.trim().toLowerCase()) || !fsType.contains(contentType)) {
                    logger.error(FmsCodeBean.FmsCode.ERROR_FILETYPE.code, "文件类型：" + suffix + " || " + contentType);
                    // 文件后缀错误
                    throw new AppException(FmsCodeBean.FmsCode.ERROR_FILETYPE.code);
                }
            }
        }
        //请求的文件大小限制，KB转字节
        if (allFileSize > (fileMaxRequestSize * 1024)){
            logger.error("文件的总大小为{}字节，已经超过请求限制的{}字节大小",allFileSize,fileMaxRequestSize * 1024);
            throw new AppException(FmsCodeBean.FmsCode.ERROR_FILESIZE_OUT_OFF_LIMIT.code);
        }
    }
    /**
     * 验证文件
     * @param multipartFile
     * @param fsBusinessType
     */
    @Override
    public void verifyFiles(MultipartFile multipartFile, FmsBusinessTypeResult fsBusinessType) {
        if (multipartFile == null){
            throw new AppException(FmsCodeBean.FmsCode.ERROR_FILE_IS_NULL.code);
        }
        MultipartFile[] multipartFiles = new MultipartFile[1];
        multipartFiles[0] = multipartFile;
        this.verifyFiles(multipartFiles,fsBusinessType);
    }


    /**
     * 获取文件访问路径,如果超时时间设置为小于等于0，那么返回的有效路径可以永久访问
     * @param uniqueIds
     * @param expiredTime
     * @param maxAccessCount
     * @param type
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS,readOnly = true,rollbackFor = Exception.class)
    public CommonResult<List<FmsFileRecordPathResult>> getFileUrlByFileIds(List<String> uniqueIds, Integer expiredTime, int maxAccessCount, String type) {

        if (CollectionUtils.isEmpty(uniqueIds)){
            logger.error(FmsCodeBean.FmsCode.NOT_EXIST_FILE.message,new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code, FmsCodeBean.FmsCode.NOT_EXIST_FILE.message));
            return CommonResult.success(new ArrayList<>());
        }
        //根据uniqueIds查询任务记录
        FmsRecordDTO fmsRecordDTO = new FmsRecordDTO();
        fmsRecordDTO.setUniqueIds(uniqueIds);
        List<FmsRecordDTO> fsUploadRecords = fmsFileRecordMapper.selectbySelective(fmsRecordDTO);
        if (CollectionUtils.isEmpty(fsUploadRecords)) {
            logger.error(FmsCodeBean.FmsCode.NOT_EXIST_FILE.message,new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code, FmsCodeBean.FmsCode.NOT_EXIST_FILE.message));
            return CommonResult.success(new ArrayList<>());
        }
        //返回 uniqueId--文件path
        List<FmsFileRecordPathResult> recordPathResults = new ArrayList<>();
        //文件访问令牌的批量添加
        List<FmsAccessTokenDTO> fssAccessTokens = new ArrayList<>();
        for (FmsRecordDTO fssRecord : fsUploadRecords){
            String uniqueId = fssRecord.getUniqueId();
            String fileAccessToken = UUIDUtils.uuid();
            //String fileAccessUrlForOutter = "http://1270.0.0.1:8080/fms/get/file/";//本地路径
            String path = fileAccessUrlForOutter + uniqueId + "?fileAccessToken=" + fileAccessToken;
            // 缓存文件token
            FmsAccessTokenResult fmsAccessTokenResult = new FmsAccessTokenResult(expiredTime, maxAccessCount, uniqueId, type);
            if (expiredTime > 0){
                redisTemplate.opsForValue().set(fileAccessToken , JSON.toJSONString(fmsAccessTokenResult) , expiredTime , TimeUnit.MINUTES);
            }else {
                FmsAccessTokenDTO fssFmsAccessTokenDto = new FmsAccessTokenDTO();
                fssFmsAccessTokenDto.setFileAccessToken(fileAccessToken);
                fssFmsAccessTokenDto.setType(type);
                fssFmsAccessTokenDto.setUniqueId(uniqueId);
                fssAccessTokens.add(fssFmsAccessTokenDto);
            }
            FmsFileRecordPathResult fmsFileRecordPathResult = new FmsFileRecordPathResult();
            fmsFileRecordPathResult.setPath(path);
            fmsFileRecordPathResult.setUniqueId(uniqueId);
            recordPathResults.add(fmsFileRecordPathResult);
        }
        if (!CollectionUtils.isEmpty(fssAccessTokens)){
            fmsAccessTokenMapper.multipartInsert(fssAccessTokens);
        }
        return CommonResult.success(recordPathResults);
    }

    /**
     * 下载文件
     *  @param fileAccessToken
     * @param response
     * @param uniqueId
     */
    @Override
    public void downloadFile(String fileAccessToken, HttpServletResponse response, String uniqueId) {
        try {
            FmsAccessTokenResult accessToken = fmsAccessTokenService.getFileAccessToken(fileAccessToken);
            if (accessToken == null) {
                throw new AppException(FmsCodeBean.FmsCode.INVALID_FS_TOKEN.code);
            }
            if (!uniqueId.equals(accessToken.getUniqueId())) {
                throw new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code);
            }
            FmsAccessTokenParam param = new FmsAccessTokenParam();
            BeanUtils.copyProperties(accessToken , param);
            //获得uniqueId对应的文件上传记录
            FmsDetailRecordResult fsUploadRecord = this.getFileUploadRecord(param, uniqueId, fileAccessToken);
            String type = accessToken.getType();
            if (FmsConstants.SHOW.equals(type)) {
                response.addHeader("Content-Length", "" + fsUploadRecord.getFileSize());
            } else if (FmsConstants.DOWNLOAD.equals(type)) {
                // 设置文件名, 可用于下载
                response.addHeader("Content-Length", "" + fsUploadRecord.getFileSize());
                response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fsUploadRecord.getOrigFileName(), "utf-8"));
            }
            response.setCharacterEncoding("UTF-8");
            response.setContentType(fsUploadRecord.getContentType());
            OutputStream outputStream = response.getOutputStream();
            //根据uniqeId和文件名得到本地文件存储路径
            FileCopyUtils.copy(new FileInputStream(this.getFileLocalPath(fsUploadRecord.getUniqueId() , fsUploadRecord.getOrigFileName())), outputStream);
        } catch (AppException e) {
            String errorCode = e.getErrorCode();
            response.setStatus(Integer.parseInt(errorCode));
            return;
        } catch (IOException e) {
            response.setStatus(500);
            return;
        }
    }

    /**
     * 获取base64文件
     *
     * @param uniqueId
     * @return
     */
    @Override
    public CommonResult<String> getFileBase64(String uniqueId) {
        FmsDetailRecordResult fsUploadRecord = this.getFileUploadRecord(uniqueId);
        try {
            String path = this.getFileLocalPath(fsUploadRecord.getUniqueId() , fsUploadRecord.getOrigFileName());
            File file = new File(path);
            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[(int)file.length()];
            fileInputStream.read(buffer);
            fileInputStream.close();
            return CommonResult.success(new BASE64Encoder().encode(buffer));
        } catch (Exception e) {
            logger.error(FmsCodeBean.FmsCode.ERROR_TRANS_BASE64.code, e);
            throw new AppException(FmsCodeBean.FmsCode.ERROR_TRANS_BASE64.code, FmsCodeBean.FmsCode.ERROR_TRANS_BASE64.message);
        }
    }

    /**
     * 上传base64文件
     *
     * @param base64Req
     * @return
     */
    @Override
    public CommonResult<String> uploadFileBase64(FmsBase64FileUploadRequest base64Req) {
        FmsUploadTokenResult fsUploadToken = fmsBusinessTypeService.getFsBusinessTypeByUploadToken(base64Req.getUploadToken());
        if (fsUploadToken == null) {
            throw new AppException(FmsCodeBean.FmsCode.ERROR_TIMEOUT.code);
        }
        FmsBusinessTypeResult fsBusinessType = fsUploadToken.getBusinessTypeResult();
        byte[] fileBytes;
        try {
            //将字符串转换为byte数组
            fileBytes = Base64Utils.decode(base64Req.getFileBaseSixFour());
        } catch (Exception e) {
            throw new AppException(FmsCodeBean.FmsCode.FAIL.code);
        }

        String fsSize = fsBusinessType.getFsSize();
        Long limitSize = Long.parseLong(fsSize);
        long fileSize = fileBytes.length;
        int mult = 1024;
        if (fileSize > (limitSize * mult)) {
            throw new AppException(FmsCodeBean.FmsCode.ERROR_FILESIZE_OUT_OFF_LIMIT.code);
        }
        if (!fsBusinessType.getBusinessType().equals(base64Req.getBusinessType())) {
            // 文件类型不一致
            throw new AppException(FmsCodeBean.FmsCode.ERROR_FILETYPE.code);
        }
        FmsBusinessTypeParam fmsBusinessTypeParam = new FmsBusinessTypeParam();
        BeanUtils.copyProperties(fsBusinessType , fmsBusinessTypeParam);
        FmsUploadTokenParam fmsUploadTokenParam = new FmsUploadTokenParam();
        BeanUtils.copyProperties(fsUploadToken , fmsUploadTokenParam);
        String fileUploadResponse = this.saveFileFromBytes(fileBytes, base64Req.getFileName(), fmsBusinessTypeParam, fmsUploadTokenParam);
        fmsBusinessTypeService.deleteFsBusinessTypeByUploadToken(base64Req.getUploadToken());
        return CommonResult.success(fileUploadResponse);
    }

    /**
     * 下载zip压缩文件接口
     *  @param uniqueIds
     * @param zipName
     * @param response
     */
    @Override
    public void downloadZipByUniqueIds(List<String> uniqueIds, String zipName, HttpServletResponse response) {
        if (com.itools.core.utils.StringUtils.isBlank(zipName)) {
            zipName = "attachment";
        }
        List<String> paths = new ArrayList<>();
        List<File> files = new ArrayList<>();
        for (String uniqueId : uniqueIds) {
            FmsDetailRecordResult fsUploadRecord = this.getFileUploadRecord(uniqueId);
            String path = this.getFileLocalPath(uniqueId, fsUploadRecord.getOrigFileName());
            paths.add(path);
            File file = new File(path);
            files.add(file);
        }
        try {
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; fileName=" + URLEncoder.encode(zipName + ".zip", "utf-8"));
            OutputStream outputStream = response.getOutputStream();
            ZipUtils.toZip(files, outputStream);
        } catch (FileNotFoundException e) {
            throw new AppException(FmsCodeBean.FmsCode.NOT_EXIST_FILE.code);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
